package modelo.carrera;

import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;


public abstract class ObservadorDeCarrera implements Observer {
	protected long instante;
		
	protected abstract class Delegado {
		protected ObservadorDeCarrera observador;
		
		public Delegado(ObservadorDeCarrera observador) {
			this.observador = observador;
		}
		public ObservadorDeCarrera getObservador() {
			return observador;
		}
		
		public abstract void hacer(Carrera carrera); 
	}
	
	protected Map<Carrera.Eventos, Delegado> delegados;
	
	protected Map<Carrera.Eventos, Delegado> getDelegados() {
		if (delegados == null) {
			delegados = new HashMap<Carrera.Eventos, Delegado>();
			delegados.put(Carrera.Eventos.InicioDeCarrera, new IniciarJuego(this));
			delegados.put(Carrera.Eventos.SiguienteInstante, new enInstante(this));
			delegados.put(Carrera.Eventos.FinDeCarrera, new enFinDeJuego(this));
		}
		return delegados;
	}
	
	protected Delegado obtenerDelegado(Carrera.Eventos evento) {
		return getDelegados().get(evento);
	}
	
	protected class IniciarJuego extends Delegado {
		public IniciarJuego(ObservadorDeCarrera observador) {
			super(observador);
		}
		
		@Override
		public void hacer(Carrera carrera) {
			observador.enInicioDeJuego();
			observador.armarMapa(carrera);
			observador.iniciarInstante();
		}
	}
	
	protected class enInstante extends Delegado {
		public enInstante(ObservadorDeCarrera observador) {
			super(observador);
		}
		
		@Override
		public void hacer(Carrera carrera) {
			observador.armarTick(carrera, observador.getInstante());
			observador.incrementarInstante();
		}
	}
	
	protected class enFinDeJuego extends Delegado {
		public enFinDeJuego(ObservadorDeCarrera observador) {
			super(observador);
		}
		
		@Override
		public void hacer(Carrera carrera) {
			observador.enFinDeJuego();
		}
	}
	
	
	protected long getInstante() {
		return instante;
	}
	
	protected void iniciarInstante() {
		instante = 0;
	}
	
	protected void incrementarInstante() {
		instante++;
	}

	public void actualizar(Carrera carrera, Carrera.EventoConcreto evento) {
		Delegado delegado = obtenerDelegado(evento.getTipo());
		delegado.hacer(carrera);
	}
	
	@Override
	public void update(Observable o, Object arg) {
		actualizar((Carrera)o, (Carrera.EventoConcreto)arg);
	}
	
	protected void enFinDeJuego() {};
	protected void enInicioDeJuego() {};
	protected void enNuevoInstante() {};

	protected abstract void armarMapa(Carrera carrera);
	protected abstract void armarTick(Carrera carrera, long instante);

}
